#ifndef HTTP_CONNECTION_H
#define HTTP_CONNECTION_H

#include <time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "buffer.h"
#include "rangelist.h"
#include "headers.h"

#if defined(TCP_NOPUSH) && !defined(TCP_CORK)
#define TCP_CORK TCP_NOPUSH
#endif /* defined(TCP_NOPUSH) && !defined(TCP_CORK) */

typedef enum {
		READING_REQUEST,
		WRITING_MESSAGE_HEADERS,
		WRITING_ERROR_PAGE,
		WRITING_DIRECTORY_LISTING,
		WRITING_RANGE,
		WRITING_BOUNDARY,
		CONNECTING_TO_BACKEND,
		SENDING_REQUEST_TO_BACKEND,
		SENDING_POST_TO_BACKEND,
		READING_RESPONSE_HEADER_FROM_BACKEND,
		READING_RESPONSE_FROM_BACKEND,
		WAITING_TO_BACKEND,
		READING_POST_FROM_CLIENT,
		WRITING_RESPONSE_TO_CLIENT,
		CONNECTING_TO_FCGI_SERVER,
		SENDING_REQUEST_TO_FCGI_SERVER,
		SENDING_POST_TO_FCGI_SERVER,
		READING_RESPONSE_HEADER_FROM_FCGI_SERVER,
		READING_RESPONSE_FROM_FCGI_SERVER,
		WAITING_TO_FCGI_SERVER,
		FCGI_READING_POST_FROM_CLIENT,
		FCGI_WRITING_RESPONSE_TO_CLIENT
} eHttpConnectionState;

typedef enum {ERROR_PAGE_RESPONSE, DIRECTORY_LISTING_RESPONSE, FILE_RESPONSE} eResponseType;

typedef enum {
		NO_METHOD = -1,
		CONNECT_METHOD,
		COPY_METHOD,
		DELETE_METHOD,
		GET_METHOD,
		HEAD_METHOD,
		LOCK_METHOD,
		MKCOL_METHOD,
		MOVE_METHOD,
		OPTIONS_METHOD,
		POST_METHOD,
		PROPFIND_METHOD,
		PROPPATCH_METHOD,
		PUT_METHOD,
		TRACE_METHOD,
		UNLOCK_METHOD
} eHttpMethod;

typedef struct {
	int index; /* Position of connection in index array. */

	int sd; /* Socket descriptor. */
	int fd; /* File descriptor. */

	off_t filesize;
	struct tm fmtime;
	time_t if_modified_since;

	struct sockaddr_in sin;

	void *backend_list;
	off_t backend;
	size_t connection_attempts;

	buffer_t buffer;

	eHttpConnectionState http_connection_state;
	int request_state;

	eHttpMethod http_method;
	buffer_t path;
	buffer_t host;
	buffer_t authorization;
	buffer_t user_agent;
	buffer_t message_body;
	buffer_t content_type;

	off_t query;

	unsigned short requestId;
	size_t size_next_record;
	size_t pending_current_record;
	unsigned char padding;
	size_t record_content_length;
	int fastcgi_state;
	char added_empty_stdin;

	unsigned int ip;

	buffer_t range_header;
	buffer_t *parts;
	rangelist_t rangelist;
	size_t current_range;

	unsigned major_number;
	unsigned minor_number;
	unsigned nCRLFs;

	eMessageHeader message_header;
	unsigned int headers;

	off_t token;
	off_t end;
	off_t offset;

	off_t header_offsets[5][2];
	unsigned char noffsets;

	off_t status_code_offset;

	ssize_t content_length;

	time_t creation;
	size_t sent;
	size_t received;

	time_t last_read_write; /* Time at which the client got connected or last time we got data from him or sent data to him. */

	eResponseType response_type;

	unsigned short port;

	char keep_alive;
	char chunked;

	char proxy_connection;

#if defined(HAVE_KQUEUE) || defined(HAVE_PORT)
	int filter;
#endif /* HAVE_KQUEUE || HAVE_PORT */
} http_connection_t;

void http_connection_init (http_connection_t *http_connection);
void http_connection_free (http_connection_t *http_connection);

void http_connection_reset (http_connection_t *http_connection);

void http_connection_reuse (http_connection_t *http_connection);

#define http_connection_add_header(http_connection, message_header) (http_connection)->headers |= (1 << (message_header))
#define http_connection_is_header_present(http_connection, message_header) (http_connection)->headers & (1 << (message_header))

#endif /* HTTP_CONNECTION_H */
